//
//  Route.swift
//  DaFit
//
//  Created by sylar on 2018/5/3.
//  Copyright © 2018年 sylar. All rights reserved.
//

import Foundation
import UIKit
import MapKit
import MYModule



public typealias GPSTrainingModel = TrainingModel



extension GPSTrainingModel {
    
    public static let distanceFilter: CLLocationDistance = 0.1

    public func addLocation(location: CLLocation?) -> Bool {

        guard let location = location else {
            return false
        }
        
        // 坐标系统一转成地球坐标系
        let coordinate = location.coordinate.GCJLocationCoordinate()
        let newLocal = CLLocation(coordinate: coordinate, altitude: location.altitude, horizontalAccuracy: location.horizontalAccuracy, verticalAccuracy: location.verticalAccuracy, course: location.course, speed: location.speed, timestamp: location.timestamp)
        
        if let lastLocation = locations.last, let clLocation = lastLocation.clLocation {
            
            let distance: CLLocationDistance = clLocation.distance(from: newLocal)
            let newTime = newLocal.timestamp.timeIntervalSince1970
            let lastTime = clLocation.timestamp.timeIntervalSince1970

            if distance < GPSTrainingModel.distanceFilter || newTime <= lastTime {
                return false
            }
        }
        
        let traininglocation = TrainingLocation(location: newLocal)
        locations.append(traininglocation)
        self.updateData()

        return true
    }

    public func title() -> String {

        let formatter: DateFormatter = DateFormatter()
        formatter.timeZone = NSTimeZone.local
        formatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
        formatter.calendar = Calendar.init(identifier: .gregorian)
        let date = Date(timeIntervalSince1970: TimeInterval(self.startTime))
        return formatter.string(from: date)
    }

    public func getTime() -> String {
        let formatter: DateFormatter = DateFormatter()
        formatter.timeZone = NSTimeZone.local
        formatter.dateFormat = getHistoryTimeFormat()
        formatter.calendar = Calendar.init(identifier: .gregorian)
        let date = Date(timeIntervalSince1970: TimeInterval(self.startTime))
        return formatter.string(from: date)
    }

    public func detail() -> String {
        return NSString(format: "p: %d, d: %.2fm, t: %@", locations.count, distance, formattedDuration(duration: totalDuration())) as String
    }
    public func currentSpeed() -> Double {
        
        if locations.count > 1 {
            
            guard let currentLocation = locations.last else {
                return 0
            }
            if currentLocation.speed > 0 {
                let speed = (1000 / 60) / currentLocation.speed
                if speed > 10000 {
                    return 0
                } else {
                    return 0
                }
            }
        }
        return 0
    }

    public func getSpeed() -> Double {
        
        var speed: Double = 0
        guard let currentLocation = locations.last else { return speed }
        if currentLocation.speed > 0 {
            if Unit.sharedInstance.myType == .imperial {
                
                speed = (1000 / 60) / (currentLocation.speed)
                
            } else {
                speed = (1609.3 / 60) / (currentLocation.speed)
            }
        }
        
        print("当前速度 speed = \(speed)")
        
        return speed
    }

    public func currentSpeedForImperial() -> Double {
        if locations.count > 1 {
            guard let currentLocation = locations.last else {
                return 0
            }
            if currentLocation.speed > 0 {
                let speed = (1609.3 / 60) / currentLocation.speed
                if speed > 10000 {
                    return 0
                } else {
                    return speed
                }
            }
        }
        return 0
    }
    
    public func avgSpeedMinute() ->CLLocationSpeed {
        
        if distance > 0 {
            let avgSpeed = (totalDuration() / 60.0) / (Double(distance) / 1000.0)
            return avgSpeed
        } else {
            return 0
        }
        
        
    }

    public func avgSpeedString() -> String {
        var avgSpeed: CLLocationSpeed = 0
        if locations.count > 1 {
            avgSpeed = avgSpeedMinute()
            return String(format: "%d' %d''",Int(avgSpeed), Int((avgSpeed * 100)) % 100)
        }
        return "00'00''"

    }

    public func avgSpeedForImperial() -> String {
        var avgSpeed: CLLocationSpeed = 0
        if locations.count > 1, distance > 0 {
            avgSpeed = (totalDuration() / 60.0) / (Double(distance) / 1700.0)
            return String(format: "%d' %d''",Int(avgSpeed), Int((avgSpeed * 100)) % 100)
        }
        return "00'00''"

    }



    public func startLocation() -> CLLocation? {
        return locations.first?.clLocation
    }

    public func endLocation() -> CLLocation? {
        return locations.last?.clLocation
    }

    public func totalDistance() -> CLLocationDistance {

        var distance: CLLocationDistance = 0
        if locations.count > 1 {

            var currentLocation: CLLocation? = nil

            for location in locations {

                let clLocation = location.clLocation
                if let currentLocation = currentLocation, let clLocation = clLocation {
                    let d = clLocation.distance(from: currentLocation)
//                    print(d)
                    distance += d
                    
                    
                }
                currentLocation = clLocation
                
            }

        }

        return distance
    }

    public func totalDuration() -> TimeInterval {
        
        return TimeInterval(vaildTime)
        
//        return TimeInterval(endTime - startTime)
    }

    public func formattedDuration(duration: TimeInterval) -> String {

        var component: [Double] = [0, 0, 0]
        var t = duration

        for i in 0 ..< component.count {
            component[i] = Double(Int(t) % 60)
            t /= 60.0
        }

        return NSString(format: "%02d:%02d:%02d", lround(component[2]) , lround(component[1]), lround(component[0])) as String
    }

    public func coordinates() -> [CLLocationCoordinate2D] {

        var coordinates: [CLLocationCoordinate2D] = []
        if locations.count > 1 {

            for location in locations {

                let loc = CLLocation.initWith(trainingLocation: location)

                if self.isChina == true {
                    coordinates.append(loc.coordinate.GCJLocationCoordinate())
                }
                else {
                    coordinates.append(loc.coordinate)
                }
            }
        }
        return coordinates
    }
    
    
    public func stepSpeed() ->Int {
        var speed = 0
        let times = totalDuration() / 60
        if times > 0 {
            speed = Int(Double(step)/times)
        }
        return speed
    }
    
    
    public func getElevation() -> String {
        
        guard let elevationDouble = Double(elevation) else { return "--" }
        

        
        
        return elevationDouble.normalTitle
    }
    
    /// 计算爬升高度
    /// - Returns:
    public func calculateElevation(locations: [TrainingLocation]) ->String {
        
        /// 假数据单元测试
        //        let locastions = ( 0 ..< 10).map { i ->TrainingLocation in
        //
        //            var model = TrainingLocation()
        ////            model.altitude = i%3 == 0 ? (10.0 + Double(i)) : (-5.0 - Double(i))
        //            model.altitude = Double(i)
        //            return model
        //        }
        //
        //        calculateElevation(locations: locastions)
        
        print(locations.map({ $0.altitude }))
        
        var elevation: Double = 0
        
        if locations.count < 2 {
            
            return "\(elevation)"
        }
        
        var minLocation: TrainingLocation?
        var maxLocation: TrainingLocation?
        
        for i in 1 ..< (locations.count - 1) {
            
            let last = locations[i - 1]
            let current = locations[i]
            let next = locations[i + 1]
            
            if current.altitude >= last.altitude && current.altitude > next.altitude {
                maxLocation = current
            }
            // 最后一个还是上坡
            if i == locations.count - 2 && current.altitude < next.altitude {
                maxLocation = next
            }
            
            if current.altitude <= last.altitude && current.altitude < next.altitude {
                minLocation = current
            }
            
            // 第一段是下坡
            if i == 1 && current.altitude > last.altitude {
                minLocation = last
            }
            
            if let max = maxLocation, let min = minLocation {
                
                if min.altitude < max.altitude {
                    elevation += max.altitude - min.altitude
                }
                
                print("----")
                print(max.altitude)
                print(min.altitude)
                print(elevation)
                print("----")
                
                maxLocation = nil
                minLocation = nil
            }
        }

        print("爬升高度 elevation = \(elevation)")
        return "\(elevation)"
        
    }

    public func getMinHR() -> Int{
        var minHR = 0
        if heartRates.count > 0{
            for h in heartRates{
                if minHR == 0{
                    minHR = h
                }else {
                    if h < minHR{
                        minHR = h
                    }
                }
            }
        }
        return minHR
    }

    public func getAvgHR() -> Int{
        var avgHR = 0
        var count = 0
        var total = 0
        if heartRates.count > 0{
            for h in heartRates {
                if h > 0{
                    total += h
                    count += 1
                }
            }
            if count > 0{
                avgHR = total / count
            }
        }
        return avgHR
    }

    public func getMaxHR() -> Int{
        var maxHR = 0
        if heartRates.count > 0 {
            for h in heartRates {
                if h > maxHR{
                    maxHR = h
                }
            }
        }
        return maxHR
    }
    
    
    /// 刷新是否目标达成
    /// - Parameters:
    ///   - goalType: 目标设置类型
    ///   - goal: 目标值，注意： 距离单位是 km
    public func reloadAchievement(with goalType: TrainingTypeContent, goal: Double) ->Bool {
        
        var achievement = false
        switch goalType {
       
        case .distance:
            
            achievement = Double(self.distance) >= goal * 1000
            break
            
        case .time:
            
            achievement = self.totalDuration() >= goal
            break
            
        case .pace:
            
            achievement = self.avgSpeedMinute() < goal
            break
            
        case .calorie:
            
            achievement = Double(self.kcal) >= goal
            break
            
        default:
            achievement = false
            break
        }
        
        return achievement
        
    }
}

public func transformFormWGSToGCJ(_ wgsLoc: CLLocationCoordinate2D) -> CLLocationCoordinate2D{
    var gcjLoc: CLLocationCoordinate2D = CLLocationCoordinate2D()
    if isLocationOutOfChina(wgsLoc) {
        gcjLoc = wgsLoc
    }else {
        var adjustLat: Double = transformLat(x: wgsLoc.longitude - 105.0, y: wgsLoc.latitude - 35.0)
        var adjustLon: Double = transformLon(x: wgsLoc.longitude - 105.0, y: wgsLoc.latitude - 35.0)
        let radLat = wgsLoc.latitude / 180.0 * π
        var magic = sin(radLat)
        magic = 1 - ee * magic * magic
        let sqrtMagic: Double = sqrt(magic)
        adjustLat = (adjustLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * π)
        adjustLon = (adjustLon * 180.0) / (a / sqrtMagic * cos(radLat) * π)
        gcjLoc.longitude = wgsLoc.longitude + adjustLon
        gcjLoc.latitude = wgsLoc.latitude + adjustLat
    }
    return gcjLoc
}

public func transformLat(x: Double ,y: Double) -> Double{
    let tempSqrtLat = 0.2 * sqrt(abs(x))
    var lat: Double = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + tempSqrtLat
    lat += (20.0 * sin(6.0 * x * π) + 20.0 * sin(2.0 * x * π)) * 2.0 / 3.0
    lat += (20.0 * sin(y * π) + 40.0 * sin(y / 3.0 * π)) * 2.0 / 3.0;
    lat += (160.0 * sin(y / 12.0 * π) + 320 * sin(y * π / 30.0)) * 2.0 / 3.0;
    return lat
}

public func transformLon(x: Double, y: Double) -> Double {
    let tempSqrtLon = 0.1 * sqrt(abs(x))
    var lon: Double = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + tempSqrtLon
    lon += (20.0 * sin(6.0 * x * π) + 20.0 * sin(2.0 * x * π)) * 2.0 / 3.0
    lon += (20.0 * sin(x * π) + 40.0 * sin(x / 3.0 * π)) * 2.0 / 3.0
    lon += (150.0 * sin(x / 12.0 * π) + 300.0 * sin(x / 30.0 * π)) * 2.0 / 3.0
    return lon
}

public func isLocationOutOfChina(_ location: CLLocationCoordinate2D) -> Bool {
    if location.longitude<72.004 || location.longitude>137.8347 || location.latitude<0.8293 || location.latitude>55.8271 {
        return true
    }
    return false
}

public extension CLLocationCoordinate2D {
    func GCJLocationCoordinate() -> CLLocationCoordinate2D {
        let coordinate = transformFormWGSToGCJ(self)
        return coordinate
    }
}

public func getHistoryTimeFormat() -> String{
    var format = "yyyy-MM-dd\nHH:mm"
    let formatStrigForHours = DateFormatter.dateFormat(fromTemplate: "j", options: 0, locale: Locale.current)
    if let hasAMPM = formatStrigForHours?.contains("a"), hasAMPM == true{
        if amIsFront(){
            format = "yyyy-MM-dd\na hh:mm"
        }else {
            format = "yyyy-MM-dd\nhh:mm a"
        }
    }
    return format
}

public func amIsFront() ->Bool{
    if let formatStringForHours = DateFormatter.dateFormat(fromTemplate: "j", options: 0, locale: Locale.current){
        let index = formatStringForHours.positionOf(sub: "a")
        if index > -1 && index < 2 {
            return true
        }
    }
    return false
}

public let π: Double = CGFloat.pi
public let ee: Double = 0.00669342162296594323
public let a: Double = 6378245.0

